from PyQt5 import QtCore
import decimal
import logging
import os
import time
import datetime
import math
import copy
import base64
import re
import subprocess
import ctypes
import traceback
import requests
# 创建全局Session对象
global_session = requests.Session()
# 设置全局超时
global_session.timeout = (5, 30)
DEMO=0
config=None 
mainw=None  #mainw
server=None #dblims
cali=None  #cali dialog
db=None  #calidb
dbopen=False
methodid=None #spec 校正曲线
now=datetime.datetime.now()
now_date= now.strftime("%Y-%m-%d")
temp_cali=None #温度校正表
# key = b'PpgJcrAgnpSbw2WtP_hwAqi33C3YMoD0zvFY7H-QPnc='# 生成 16 字节的密钥
start_time=time.perf_counter()
year=datetime.datetime.now().year % 2000
lastYear=year-1
year=str(year)
lastYear=str(lastYear)
sample_name_pattern="(%s|%s)[A-Z][A-Z][0-9]*-[0-9]*(_[0-9]*)?" % (year,lastYear)
def filenamepattern(filename,p):
    p1=p.replace(".",r"\.")
    p2=p1.replace("*",".*")
    p2=p2+"$"
    if re.search(p2,filename,re.IGNORECASE)==None :
       return False
    else:
       return True
def get_newest_files(p,filepattern):
    rt=[]
    n_tm=time.time()
    if os.path.exists(p):
        pass
    else:
        logging.info(f"{p} not exists")
        return None
    walk_tree = os.walk(p)
    for root, subFolders, files in walk_tree:
        for file in files:
            fname=os.path.join(root,file)
            if filenamepattern(fname,filepattern):
                tm=os.path.getmtime(fname)
                rt.append((tm,fname))
            else:
                pass
    if len(rt)>0:
        rt.sort(reverse=True)
        tm=None
        res=[]
        for i in range(len(rt)):
            print(tm,rt[i][0],rt[i][1])
            if i==0:
                tm= rt[i][0]
                res.append(rt[i][1])
            elif tm-rt[i][0]<60:
                res.append(rt[i][1])
            else:
                break
        return res
    else:
        return None

def get_newest_file_n(p,filepattern,n):
    rt=[]
    n_tm=time.time()
    if os.path.exists(p):
        pass
    else:
        logging.info(f"{p} not exists")
        return None
    walk_tree = os.walk(p)
    for root, subFolders, files in walk_tree:
        for file in files:
            fname=os.path.join(root,file)
            if filenamepattern(fname,filepattern):
                tm=os.path.getmtime(fname)
                rt.append((tm,fname))
            else:
                pass
    print(len(rt))
    if len(rt)>0:
        rt.sort()
        return rt[-n:]
    else:
        return None
def get_console_encode():
    kernel32 = ctypes.windll.kernel32
    console_cp = kernel32.GetConsoleOutputCP()
    if console_cp==936:
        return "gbk"
    elif console_cp==65001:
        return "utf-8"# → UTF-8
    elif console_cp==437:# → 
        return "ibm437"#（英文默认）
    elif console_cp==1252:# → 
        return "windows-1252"#（西欧）
    else:
        return "utf-8"
def xcopy_one(SRC_PATH,DEST_PATH):
    args = ["xcopy", "/Y",os.path.abspath(SRC_PATH),os.path.abspath(DEST_PATH)+"\\"]
    logging.info(args)
    p=subprocess.Popen(args, creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP,
                     stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, close_fds=True,
    bufsize=1)
    code=locale.getlocale()[1]
    for line in iter(p.stdout.readline, b''):
        try:
            print(line.decode(code))
        except UnicodeDecodeError:
            pass
    p.stdout.close()
    p.wait()
def xcopy(src_path,dest_path):
    args = ["xcopy.exe", "/Y", "/S", os.path.abspath(src_path), os.path.abspath(dest_path)+"\\"]
    logging.info("Executing: %s", args)
    try:
        with subprocess.Popen(args, 
                            creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            close_fds=True,
                            bufsize=1,
                            text=True,  # Automatic decoding
                            encoding=get_console_encode(),  # Explicit encoding
                            errors='replace'  # Handle decode errors
                            ) as p:
            
            # Read both stdout and stderr to prevent deadlocks
            for line in p.stdout:
                logging.info(line.rstrip())
            for line in p.stderr:
                logging.error(line.rstrip())
                
            return_code = p.wait(timeout=60)  # Add timeout
            if return_code != 0:
                logging.error("xcopy failed with return code %d", return_code)
    except subprocess.TimeoutExpired:
        logging.error("xcopy timed out")
        p.kill()
    except Exception as e:
        logging.error("Error executing xcopy: %s", str(e))        
def copy_here(src_path):
    args = ["xcopy.exe", "/Y", "/S", os.path.abspath(src_path), getpath.getpath()]
    logging.info("Executing: %s", args)
    try:
        with subprocess.Popen(args, 
                            creationflags=subprocess.DETACHED_PROCESS | subprocess.CREATE_NEW_PROCESS_GROUP,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            close_fds=True,
                            bufsize=1,
                            text=True,  # Automatic decoding
                            encoding=get_console_encode(),  # Explicit encoding
                            errors='replace'  # Handle decode errors
                            ) as p:
            
            # Read both stdout and stderr to prevent deadlocks
            for line in p.stdout:
                logging.info(line.rstrip())
            for line in p.stderr:
                logging.error(line.rstrip())
                
            return_code = p.wait(timeout=60)  # Add timeout
            if return_code != 0:
                logging.error("xcopy failed with return code %d", return_code)
    except subprocess.TimeoutExpired:
        logging.error("xcopy timed out")
        p.kill()
    except Exception as e:
        logging.error("Error executing xcopy: %s", str(e))

def isSampleName_0(name):
    if re.match(sample_name_pattern,name.strip())!=None:
        return True# name.strip()[0:0+2]=="23":
    else:
        return False
def isSampleName(name):
    """
    判断给定的名称是否符合样本名称的格式（前2位数字 + 特定格式）。
    
    :param name: 待判断的名称
    :return: 如果符合格式返回 True，否则返回 False
    """
    # 定义样本名称的正则表达式模式（前2位数字 + 后续格式）
    # 修改为允许没有-之后的字符
    sample_name_pattern = r"^\d{2}[A-Z]{2}\d*(?:-\d*(?:_\d*)?)?$"
    
    # 检查是否匹配
    if re.match(sample_name_pattern, name.strip()) is not None:
        return True
    else:
        return False
def getdb():
    if mainw is None:
        return None
    db=mainw.db.__module__
    return  db
def cha_value(cali,k):
    logging.info(cali)
    logging.info(k)
    v2=None
    for i in range(len(cali)):
        try:
            (k2,v2)=cali[i]
            if i==0: 
                if k<k2:
                    return v2
                else:
                    continue
            if k<k2:
                (k1,v1)=cali[i-1]
                return (k-k1)/(k2-k1)*(v2-v1)+v1
        except TypeError:
            logging.info(traceback.format_exc())
    return v2
def make_valid_filename(filename):
    invalid_chars = ['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>', '.']
    valid_filename = ''
    for char in filename:
        if char not in invalid_chars:
            valid_filename += char
        else:
            valid_filename += '_'
    return valid_filename
def copy_cells(copy_from, paste_to_cell):
    """复制粘贴某个区域
    :param copy_from 复制源
    :param paste_to_cell 粘贴的左上角
    """
    # 记录边缘值
    for _copy_row in copy_from:  # 循环每一行
        for source_cell in _copy_row:  # 循环每一列
            # paste_to_cell.value = _row_cell.value
            # paste_to_cell._style = deepcopy(source_cell._style)  # 复制样式

            paste_to_cell._value = source_cell._value
            paste_to_cell.data_type = source_cell.data_type

            if source_cell.has_style:
                paste_to_cell._style = copy.copy(source_cell._style)

            if source_cell.hyperlink:
                paste_to_cell._hyperlink = copy.copy(source_cell.hyperlink)

            if source_cell.comment:
                paste_to_cell.comment = copy.copy(source_cell.comment)
            paste_to_cell = paste_to_cell.offset(row=0, column=1)  # 右移1格

        paste_to_cell = paste_to_cell.offset(row=1, column=-len(_copy_row))
def ndays_str(n):
    now=datetime.datetime.now()
    start_time=now-datetime.timedelta(days=n)
    return start_time.strftime('%Y-%m-%d %H:%M:%S')
def remove_chinese(s):
    s=s.upper()
    # start=1
    # stop=100
    # a=start
    # while a<=stop:
    #     print(a,chr(a))
    #     a+=1
    r=""
    for c in s:
        at=ord(c)
        if at>32 and at <91:
            r+=c
    return r
def mssql_getField(f):
    if f is None:
        return None
    else:
        return f.encode('latin1').decode('gbk')
def get_newest_file(p,day):
    rt=[]
    n_tm=time.time()
    walk_tree = os.walk(p)
    for root, subFolders, files in walk_tree:
        for file in files:
            fname=os.path.join(root,file)
            tm=os.path.getmtime(fname)
            if (n_tm-tm)/60.0/60.0/24>day:
                pass
            else:
                dt=datetime.datetime.fromtimestamp(tm)
                rt.append((fname,str(dt)))
    return rt
def remove_pingxing(a):
    for i in range(len(a)):
        if a[i]=="_":
            return a[:i]        
    return a
def to_float(f):
    try:
        r = float(f)
    except ValueError:
        r = 0
    return r
def timestr():
    db = QtCore.QDateTime.currentDateTime()
    db_str = db.toString("yyyy_MM_dd_hh_mm_ss")
    return db_str
def in_list(one, l1):
    for i in range(len(l1)):
        # logging.info(str(one)+str(l1[i]))
        if one in l1[i]:
            return i
    return -1
def myfloat(f):
    try:
        r = float(f)
        if math.isnan(r):
            r=0
    except ValueError:
        r = 0
    except TypeError:
        r=0
    return r
def is_int(a_string):
    try:
        int(a_string)
        return True
    except ValueError:
        return False
def is_float(a_string):
    try:
        float(a_string)
        return True
    except TypeError:
        return False
    except ValueError:
        return False    
def inItems(items, one):
    for s in items:
        if s[0] == one[0] and s[1] == one[1]:
            return True
    return False    
def isDev():
    try:
        from getmac import get_mac_address
        mac=get_mac_address()
        print(mac)
        if mac in ['00:e1:8c:ea:99:5d']:
            return True
        else:
            return False    
    except ModuleNotFoundError:
        return False
def count_decimal_places2(num):
    """
    计算输入数字的小数位数。

    :param num: 输入的数字，可以是字符串或其他可转换为 Decimal 的类型
    :return: 小数的位数
    """
    logging.info([num, type(num)])
    # 将输入转换为 Decimal 对象
    try:
        d = decimal.Decimal(str(num))
        # 返回 Decimal 对象的指数的相反数，即小数位数
        return -d.as_tuple().exponent
    except decimal.InvalidOperation:
        logging.info(traceback.format_exc())
        return 0
def valid_count(num_str,p):
    if num_str=="":
        return ""
    d=decimal.Context(prec=p,rounding=decimal.ROUND_HALF_EVEN).create_decimal(num_str)
    d=remove_exponent(d)
    r=str(d)
    return r
def point_count(num_str,p):
    if num_str=="":
        return ""
    zstr="0."+p*"0"
    logging.info([num_str,p,zstr])
    d=decimal.Decimal(num_str).quantize(decimal.Decimal(zstr),rounding=decimal.ROUND_HALF_EVEN)
    d=remove_exponent(d)
    r=str(d)
    return r
def remove_exponent(d):
    return d.quantize(decimal.Decimal(1)) if d == d.to_integral() else d.normalize()
def ppmTopc(ppmstr):
    d=decimal.Decimal(ppmstr)
    d=d/10000
    print(dir(d))
    d=remove_exponent(d)
    r=str(d)
    return r
# # 加密函数
# def encrypt_aes(plaintext):
#     cipher = AES.new(key, AES.MODE_CBC)  # 使用 CBC 模式
#     ciphertext = cipher.encrypt(pad(plaintext.encode(), AES.block_size))
#     r=cipher.iv + ciphertext  # 返回 IV + 密文
#     return r.decode("latin1")

# # 解密函数
# def decrypt_aes(ciphertext):
#     ciphertext=ciphertext.encode("latin1")
#     iv = ciphertext[:AES.block_size]  # 提取 IV
#     cipher = AES.new(key, AES.MODE_CBC, iv)
#     plaintext = unpad(cipher.decrypt(ciphertext[AES.block_size:]), AES.block_size)
#     return plaintext.decode("latin1")
def test_ctypt():
    # 测试
    plaintext = "Hello, AES encryption!"
    # 加密
    encrypted = encrypt(plaintext)
    print("加密结果:", encrypted)
    # 解密
    decrypted = decrypt(encrypted)
    print("解密结果:", decrypted)    
def encrypt(txt):
    return base64.b64encode(txt.encode("utf-8")).decode("utf-8")
def decrypt(txt):
    return base64.b64decode(txt).decode("utf-8")
if __name__=="__main__":
    # print(ppmTopc("3.12"))
    # print(get_newest_file(r"D:\Sample Results",5))
    # test_ctypt()
    print(isSampleName("25BS12-1"))
    print(isSampleName("25BS12-1_1"))
    print(isSampleName("25BS12-12_10"))
    print(isSampleName("25BS060630-3"))